1 module hip.wasm;
2 version(WebAssembly):
3 import std.meta:AliasSeq;
4 
5 ///WebAssembly.Table replacement for HipremeEngine
6 private __gshared ubyte* function(ubyte* args)[] _annonymousFunctionTable;
7 ///JSFunctions are represented opaquely right now.
8 alias JSFunction(T) = ubyte*;
9 
10 
11 ///Javascript function to call a D callback.
12 export extern(C) ubyte* __callDFunction(size_t addr, ubyte* args)
13 {
14 	return _annonymousFunctionTable[addr](args);
15 }
16 
17 ///Checks if function has been called with required arguments.
18 private ubyte* validateArguments(alias fn)(ubyte* args)
19 {
20 	import std.traits;
21 	//Only checking the count of 
22 	assert(Parameters!(fn).length <= *cast(size_t*)args, 
23 	fn.stringof~"Expected "~Parameters!(fn).length.stringof~" parameters");
24 	return args + size_t.sizeof; //Only uses 1 size_t to determine arguments validity
25 }
26 
27 
28 struct Arguments(alias Func)
29 {
30 	import std.traits;
31 	Parameters!Func params;
32 }
33 
34 /**
35  * This is a special struct, loaded with the source pointer from bridge_malloc.
36  */
37 struct WasmParametersMemory
38 {
39 	ubyte* ptr;
40 }
41 
42 Struct loadMemoryInStruct(Struct)(ubyte* arg, ubyte* rootArg)
43 {
44 	import core.stdc.string:memcpy;
45 	Struct ret;
46 	size_t last = 0;
47 	foreach(ref v; ret.tupleof)
48 	{
49 		static if(is(typeof(v) == string) || is(typeof(v) == ubyte[]))
50 		{
51 			{
52 				ubyte* data = arg+last;
53 				size_t length = *cast(size_t*)data;
54 				v = cast(typeof(v))data[size_t.sizeof..length+size_t.sizeof];
55 			}
56 		}
57 		else static if(is(typeof(v) == WasmParametersMemory))
58 			v.ptr = rootArg;
59 		else
60 			memcpy(&v, arg+last, v.sizeof);
61 		last+= v.sizeof;
62 	}
63 	return ret;
64 }
65 
66 Arguments!fn wasmParametersFromUbyte(alias fn)(ubyte* arg)
67 {
68 	return loadMemoryInStruct!(Arguments!fn)(arg, arg);
69 }
70 
71 /**
72 *	Whenever wanting to pass a callback to Javascript, call this function instead.
73 *	This function is not expected to meet usercode. But it will stay here nevertheless. 
74 */
75 ubyte* sendJSFunction(alias fn)()
76 {
77 	import std.traits;
78 	static ubyte* function(ubyte* arg) convertedFunc  = (ubyte* arg)
79 	{
80 		Arguments!fn params = wasmParametersFromUbyte!fn(validateArguments!fn(arg));
81 		static if(!is(ReturnType!fn == void))
82 			return fn(params.tupleof);
83 		else
84 		{
85 			fn(params.tupleof);
86 			return null;
87 		}
88 	};
89 	///Gets a unique function index for usage in the table. Since function addresses aren't too big, we can use a simple array.
90 	size_t addr = cast(size_t)(cast(ubyte*)fn);
91 	if(addr >= _annonymousFunctionTable.length) _annonymousFunctionTable.length = addr+1;
92 	_annonymousFunctionTable[addr] = convertedFunc;
93 
94 	return cast(ubyte*)fn;
95 }
96 
97 
98 struct JSDelegate
99 {
100 	ubyte* funcHandle;
101 	ubyte* funcptr;
102 	ubyte* ctx;
103 }
104 
105 alias JSStringType = AliasSeq!(size_t, void*);
106 
107 struct JSString
108 {
109 	size_t length;
110 	void* ptr;
111 	this(string str)
112 	{
113 		length = str.length;
114 		ptr = cast(void*)str.ptr;
115 	}
116 }
117 
118 alias JSDelegateType(T) = AliasSeq!(ubyte*, ubyte*, ubyte*);
119 
120 JSDelegate sendJSDelegate(alias dg)()
121 {
122 	import std.traits;
123 	auto convertedFunc = toFunc!dg;
124 	size_t addr = cast(size_t)cast(ubyte*)convertedFunc;
125 	if(addr >= _annonymousFunctionTable.length) _annonymousFunctionTable.length = addr+1;
126 	_annonymousFunctionTable[addr] = convertedFunc;
127 
128 
129 	return JSDelegate(cast(ubyte*)addr, cast(ubyte*)dg.funcptr, cast(ubyte*)dg.ptr);
130 }
131 
132 
133 
134 /**
135 * Generates a delegate which adds the `this` context from the arguments.
136 * It also packs the arguments sent from Javascript and transform them to the
137 * data the D delegate expects. There is also a validation in the arguments received.
138 */
139 ubyte* function(ubyte* args) toFunc(alias dg)()
140 {
141 	import std.traits;
142 	import hip.wasm;
143 	alias Params = Parameters!dg;
144 	alias DgArgs = Arguments!dg;
145 	enum Length = DgArgs.tupleof.length;
146 	alias Ret = ReturnType!dg;
147 
148 	static ubyte* function(ubyte* arg) ret = (ubyte* arg)
149 	{
150 		size_t argsCount = *cast(size_t*)arg;
151 		assert(argsCount >= 2, "D delegates expects at least 2 arguments [Function Pointer, Function Context]");
152 		assert(argsCount - 2 <= Length, "Expected "~Length.stringof~" parameters.");
153 		size_t[3] baseArgs = (cast(size_t*)arg)[0..3];
154 
155 		static DgArgs delegateArguments;
156 		static if(Length > 0)
157 			delegateArguments = loadMemoryInStruct!DgArgs(arg + size_t.sizeof*3, arg);
158 		
159 		static if(Length > 0) 
160 		{
161 			static if(is(Ret == void))
162 				void delegate(Params) dg;
163 			else
164 				ubyte* delegate(Params) dg;
165 		}
166 		else
167 		{
168 			static if(is(Ret == void))
169 				void delegate() dg;
170 			else
171 				ubyte* delegate() dg;
172 		}
173 		
174 		dg.funcptr = cast(typeof(dg.funcptr))baseArgs[1];
175 		dg.ptr = cast(void*)baseArgs[2];
176 
177 		static if(Length > 0)
178 		{
179 			static if(is(Ret == void))
180 			{
181 				dg(delegateArguments.tupleof);
182 				return null;
183 			} else return dg(delegateArguments.tupleof);
184 		}
185 		else
186 		{
187 			static if(is(Ret == void))
188 			{
189 				dg();
190 				return null;
191 			} else return dg();
192 		}
193 	};
194 	return ret;
195 }
196 
197 /**
198  * The first index of a wasm array (which is a ptr) is a size_t value containing length
199  * Params:
200  *   ptr = The ptr returnt from wasm
201  * Returns: Converted to an array
202  */
203 ubyte[] getWasmArray(ubyte* ptr)
204 {
205 	if(ptr is null)
206 		return null;
207 	size_t length = *cast(size_t*)ptr;
208 	return (ptr+size_t.sizeof)[0..length];
209 }
210 
211 
212 
213 ubyte[] getWasmBinary(ubyte* input)
214 {
215 	size_t length = *cast(size_t*)input;
216 	ubyte[] ret = (input+size_t.sizeof)[0..length];
217 	return ret;
218 }
219 
220 void freeWasmBinary(ubyte[] binary)
221 {
222 	import core.memory;
223 	ubyte* ptr = binary.ptr - size_t.sizeof;
224 	GC.free(ptr);
225 }